package la.bbs558.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import la.bbs558.context.PointContext;
import la.bbs558.controller.base.CodeEnum;
import la.bbs558.dao.BbsBoardDao;
import la.bbs558.dao.BbsClassDao;
import la.bbs558.dao.BbsPostDao;
import la.bbs558.dao.BbsReplyDao;
import la.bbs558.entity.*;
import la.bbs558.entity.vo.BbsPostVO;
import la.bbs558.entity.enums.PointsOptType;
import la.bbs558.entity.enums.PostState;
import la.bbs558.entity.enums.UserMessageType;
import la.bbs558.entity.enums.UserOpt;
import la.bbs558.exception.MsgException;
import la.bbs558.entity.params.*;
import la.bbs558.satoken.StpAdminUtil;
import la.bbs558.service.*;
import la.bbs558.util.Objs;
import la.bbs558.util.Strings;
import la.bbs558.util.WordUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

import static la.bbs558.common.Constants.YYYY_MM_DD_HH_MM_SS;

/**
 * @author: Shanks
 * @date: 2023/12/15 22:43
 * @desc:
 */
@Service
@RequiredArgsConstructor
public class BbsPostServiceImpl extends ServiceImpl<BbsPostDao, BbsPost> implements BbsPostService {

    private final BbsPostDao bbsPostDao;
    private final BbsReplyDao bbsReplyDao;
    private final BbsClassDao bbsClassDao;
    private final BbsBoardDao bbsBoardDao;
    private final UserPointsLogService userPointsLogService;
    private final WordUtil wordUtil;

    @Value("${user.default-avatar}")
    private String defaultAvatar;
    @Resource
    private UserDataService userDataService;
    @Resource
    private UserMessageService userMessageService;

    @Resource
    private BbsBoardService bbsBoardService;

    @Resource
    private BbsClassService bbsClassService;
    @Resource
    private BbsReplyService bbsReplyService;

    @Override
    public void listByPageForAdmin(PostSearchPage searchPage) {
        bbsPostDao.selectListByPageForAdmin(searchPage);
    }

    @Override
    public BbsPostVO getPostByIdForAdmin(Long id) {
        Objs.valid(id, "id 不能为空");
        return bbsPostDao.selectPostByIdForAdmin(id);
    }

    @Override
    public void removePostByIdForAdmin(Long id) {
        // 查询帖子信息
        BbsPost bbsPost = bbsPostDao.selectById(id);
        // 删除帖子和回复
        bbsPostDao.deleteById(id);
        bbsReplyDao.deleteByPostId(id);
        // board里帖子数量减一
        bbsBoardService.lambdaUpdate()
                .eq(BbsBoard::getId, bbsPost.getBoardId())
                .setSql("post_cnt = post_cnt - 1")
                .update();
        // 主题里减一
        bbsClassService.lambdaUpdate()
                .eq(BbsClass::getId, bbsPost.getClassId())
                .setSql("post_cnt = post_cnt - 1")
                .update();
    }

    @Override
    public List<BbsPost> hots() {
        return lambdaQuery()
                .select(BbsPost::getId, BbsPost::getBoardId, BbsPost::getTitle, BbsPost::getCreateTime, BbsPost::getReplyCnt)
                .eq(BbsPost::getState, 1)
                .orderByDesc(BbsPost::getReplyCnt)
                .orderByDesc(BbsPost::getCreateTime)
                .last("limit 10")
                .list();
    }

    @Override
    public List<BbsPost> news() {
        return lambdaQuery()
                .select(BbsPost::getId, BbsPost::getBoardId, BbsPost::getTitle, BbsPost::getCreateTime, BbsPost::getReplyCnt)
                .eq(BbsPost::getState, 1)
                .orderByDesc(BbsPost::getCreateTime)
                .orderByDesc(BbsPost::getReplyCnt)
                .last("limit 10")
                .list();
    }

    @Override
    public void postList(BbsPostPage page) {
        if(StpUtil.isLogin()){
            page.setUserId(StpUtil.getLoginIdAsLong());
        }
        if (page.getOrderBy() == null) {
            page.setOrderBy("1");
        }
        Objs.isTrue("desc".equalsIgnoreCase(page.getDesc()) || "asc".equalsIgnoreCase(page.getDesc()), "排序参数错误");
        getBaseMapper().queryPostList(page);
    }

    @Override
    public void searchPostByPage(PostSearchPage searchPage) {
        if (StpUtil.isLogin()) {
            searchPage.setUserId(StpUtil.getLoginIdAsLong());
        }
        bbsPostDao.searchPostByPage(searchPage);
    }

    @Override
    public BbsPost post(BbsPostParams params) {
        Long createId = params.getUserId();
        Date postTime = params.getPostTime();
        Long userId = createId == null ? StpUtil.getLoginIdAsLong() : createId;
        // 替换标签并去掉回车
        String content = params.getContent().replaceAll("<[^>]*>", "").replaceAll("[\r\n]", "");
        // TODO 暂时不检测
//        Objs.isTrue(!SensitiveWordHelper.contains(content), "内容包含敏感词");
        String title = params.getTitle();
        Objs.isTrue(title.length() <= 1024, "标题不能超过1024个字");
        Long classId = params.getClassId();
        BbsClass bbsClass = bbsClassDao.selectById(classId);
        Objs.valid(bbsClass, "主题已不存在");
        Long boardId = bbsClass.getBoardId();
        BbsBoard bbsBoard = bbsBoardDao.selectById(boardId);
        Objs.valid(bbsBoard, "版面已不存在");
        Date lastTime = postTime == null ? new Date() : postTime;
        Boolean needAudit = params.getNeedAudit();
        Integer passCode = needAudit ? PostState.WAITING.getCode() : PostState.PASS.getCode();
        BbsPost bbsPost = new BbsPost()
                .setBoardId(boardId)
                .setClassId(classId)
                .setTitle(title)
                .setCreateUser(userId)
                .setState(passCode)
                .setContent(content.substring(0, Math.min(512, content.length())))
                .setLastTime(lastTime)
                .setCreateTime(lastTime)
                .setLastUser(userId);
        boolean insert = bbsPost.insert();
        if (!insert) {
            throw new MsgException("系统错误，发帖失败");
        }
        // 将帖子放在回复表中
        Long postId = bbsPost.getId();
        new BbsReply().setPostId(postId)
                .setCreateUser(userId)
                .setContent(params.getContent())
                .setMediaJson(params.getMediaJson())
                .setCreateTime(lastTime)
                .setFirst(1)
                .insert();
        // 发帖消息审核
        if (needAudit) {
            new UserMessage()
                    .setFromUserId(StpUtil.getLoginIdAsLong())
                    .setMessageType(UserMessageType.MSG_SYS.getCode())
                    .setMsgContent("你的发帖正在审核中")
                    .setState(0)
                    .setUserId(userId)
                    .setPostId(postId)
                    .insert();
            userMessageService.websocketUpdateMsgCount(userId);
        } else {
            this.auditForAdmin(bbsPost);
        }
        return bbsPost;
    }

    @Override
    public void auditForAdmin(BbsPost bbsPost) {
        Long postId = bbsPost.getId();
        Integer isPass = Objects.equals(bbsPost.getState(), PostState.PASS.getCode()) ?
                PostState.PASS.getCode() : PostState.NOT_PASS.getCode();
        long auditId = 1;
        if (StpAdminUtil.isLogin()) {
            auditId = StpAdminUtil.getLoginIdAsLong();
        }
        BbsPost entity = new BbsPost()
                .setId(postId)
                .setReason(bbsPost.getReason())
                .setAuditUser(auditId)
                .setState(isPass);
        updateById(entity);
        BbsPost post = bbsPostDao.selectById(postId);
        // 消息推送
        Long toUser = post.getCreateUser();
        boolean insert = new UserMessage()
                .setFromUserId(auditId)
                .setMessageType(UserMessageType.MSG_SYS.getCode())
                .setMsgContent(isPass == 1 ? "你的发帖审核通过了" : "你的帖子被驳回了")
                .setAuditReason(bbsPost.getReason())
                .setState(0)
                .setUserId(toUser).setPostId(postId)
                .insert();
        if (isPass == 1) {
            // 增加积分
            PointContext context = new PointContext(toUser, bbsPost.getId(), PointsOptType.POST, UserOpt.POST);
            userPointsLogService.getPoints(context);
            // 更新发帖人的userdata
            userDataService.lambdaUpdate().setSql("post_cnt = post_cnt + 1").eq(UserData::getUserId, toUser).update();
            // 增加板面和主题帖子数量
            bbsBoardDao.updatePostCnt(post.getId());
        }
        userMessageService.websocketUpdateMsgCount(toUser);
    }

    @Override
    public void listByUserId(UserCenterSearchPage<BbsPostVO> searchPage) {
        bbsPostDao.selectListByUserId(searchPage);
    }

    @Override
    public void updateReplyCnt(Long postId) {
        getBaseMapper().updateReplyCnt(postId);
    }

    @Override
    public void highLightPost(Long id, Integer highLight) {
        BbsPost bbsPost = getById(id);
        Objs.valid(bbsPost, "帖子已不存在");
        lambdaUpdate()
                .eq(BbsPost::getId, id)
                .set(BbsPost::getHighLight, highLight == 1 ? 1 : 0)
                .set(BbsPost::getHighLightTime, new Date())
                .update();
        //获取积分
        PointContext pointContext = new PointContext(bbsPost.getCreateUser(), id, PointsOptType.HIGH_LIGHT, UserOpt.HIGH_LIGHT);
        userPointsLogService.getPoints(pointContext);
        //更新发帖人的userdata
        userDataService.lambdaUpdate().setSql("highlight_cnt = highlight_cnt + 1").eq(UserData::getUserId, bbsPost.getCreateUser()).update();
    }

    @Override
    public BbsPostVO getPostDetail(Long postId) {
        BbsPostVO bbsPostVO = bbsPostDao.selectPostDetailById(postId);
        Objs.valid(bbsPostVO, "帖子已不存在");
        if (!Objects.equals(bbsPostVO.getState(), PostState.PASS.getCode())) {
            throw new MsgException(CodeEnum.POST_AUDIT_NOT_PASS, "帖子审核未通过");
        }
        return bbsPostVO;
    }

    @Override
    public void topSectionPostList(BbsSectionPostPage page) {
        getBaseMapper().topSectionPostList(page);
        for (BbsPostVO bbsPostVO : page.getRecords()) {
            if (Objs.isInvalid(bbsPostVO.getCreateUserAvatar())) {
                bbsPostVO.setCreateUserAvatar(defaultAvatar);
            }
        }
    }

    @Override
    public BbsPost sendForAdmin(SendPostForAdminParams params) {
        Long classId = params.getClassId();
        BbsClass bbsClass = bbsClassDao.selectById(classId);
        Objs.valid(bbsClass, "主题已不存在");
        Long boardId = bbsClass.getBoardId();
        BbsBoard bbsBoard = bbsBoardDao.selectById(boardId);
        Objs.valid(bbsBoard, "版面已不存在");
        String content = params.getContent();
        if (Strings.isEmpty(content)) {
            content = wordUtil.covert2RichText(params);
        }
        params.setContent(content);
        Long userId = params.getUserId();
        String postTimeStr = params.getPostTime();
        Date postTime = new Date();
        if (Strings.isNotBlank(postTimeStr)) {
            postTime = DateUtil.parse(postTimeStr, YYYY_MM_DD_HH_MM_SS);
        }
        if (Objs.isAnyInvalid(userId, postTime, content)) {
            throw new MsgException("缺少参数！");
        }
        BbsPostParams bbsPostParams = new BbsPostParams();
        bbsPostParams.setUserId(userId);
        bbsPostParams.setClassId(params.getClassId());
        bbsPostParams.setTitle(params.getTitle());
        bbsPostParams.setContent(params.getContent());
        bbsPostParams.setMediaJson(params.getMediaJson());
        bbsPostParams.setNeedAudit(false);
        bbsPostParams.setPostTime(postTime);
        return this.post(bbsPostParams);
    }

    @Override
    public BbsReply sendReplyForAdmin(SendReplyForAdminParams params) {
        String replayTime = params.getReplayTime();
        Long postId = params.getPostId();
        Long replyId = params.getReplyId();
        if (Objs.isAllInvalid(postId, replyId)) {
            throw new MsgException("缺少必要参数");
        }
        Long userId = params.getUserId();
        String mediaJson = params.getMediaJson();
        BbsPost bbsPost = bbsPostDao.selectById(postId);
        if (bbsPost == null) {
            BbsReply bbsReply = bbsReplyDao.selectById(replyId);
            Objs.valid(bbsReply, "该回复已被删除");
            postId = bbsReply.getPostId();
            bbsPost = bbsPostDao.selectById(postId);
            Objs.valid(bbsPost, "该帖子已被删除");
        }
        Objs.isTrue(bbsPost.getState() == 1, "当前帖子正在审核中，无法回帖");
        String content = params.getContent();
        content = content.replaceAll("<[^>]*>", "");
        // 更新最后回帖人信息
        DateTime lastTime = DateUtil.parse(replayTime, YYYY_MM_DD_HH_MM_SS);
        new BbsPost()
                .setId(postId)
                .setLastTime(lastTime)
                .setLastUser(userId)
                .updateById();
        // 新增回帖信息
        BbsReply bbsReply = new BbsReply()
                .setPostId(postId)
                .setReplyId(replyId)
                .setContent(content)
                .setMediaJson(mediaJson)
                .setCreateUser(userId)
                .setCreateTime(lastTime)
                .setUpdateTime(lastTime)
                .setAuditTime(lastTime)
                .setFirst(0);
        bbsReply.insert();
        Long currentReplyId = bbsReply.getId();
        Long postCreateUser = bbsPost.getCreateUser();
        // 回复楼中楼
        if (Objs.isValid(replyId)) {
            // 更新回帖数
            bbsReplyDao.updateReplyCnt(replyId);
            BbsReply reply = bbsReplyService.getById(replyId);
            Objs.valid(reply, "回复楼层已经不存在了");
            Objs.isTrue(reply.getState() == 1, "当前回复正在审核中，无法回复");
            Long replyCreateUser = reply.getCreateUser();
            // 回复楼中楼
            bbsReplyService.sendReplyMsg(replyCreateUser, userId, currentReplyId, postId);
            //如果回复人是楼主则只发一次
            if(!Objs.equal(postCreateUser,replyCreateUser)){
                // 回复楼主
                bbsReplyService.sendReplyMsg(postCreateUser, userId, currentReplyId, postId);
            }
        } else {
            // 更新回帖数
            bbsPostDao.updateReplyCnt(postId);
            // 回复楼主
            bbsReplyService.sendReplyMsg(postCreateUser, userId, currentReplyId, postId);
        }

        // 回复统计+1
        userDataService.lambdaUpdate().setSql("reply_cnt = reply_cnt + 1").eq(UserData::getUserId, userId).update();
        // 增加积分
        PointContext context = new PointContext(userId, currentReplyId, PointsOptType.REPLY, UserOpt.REPLY);
        userPointsLogService.getPoints(context);
        return bbsReply;
    }

    @Override
    public List<BbsPost> highLights() {
        return lambdaQuery()
                .select(BbsPost::getId, BbsPost::getBoardId, BbsPost::getTitle, BbsPost::getCreateTime, BbsPost::getReplyCnt)
                .eq(BbsPost::getState, 1)
                .eq(BbsPost::getHighLight,1)
                .orderByDesc(BbsPost::getCreateTime)
                .orderByDesc(BbsPost::getReplyCnt)
                .last("limit 10")
                .list();
    }

    @Override
    public List<BbsPost> tools() {
        Optional<BbsBoard> optionalBbsBoard = bbsBoardService.lambdaQuery().eq(BbsBoard::getId, 1747163227172769792L).oneOpt();
        if(!optionalBbsBoard.isPresent()){
            optionalBbsBoard = bbsBoardService.lambdaQuery().eq(BbsBoard::getBoardName, "558项目及工具测评").last("limit 1").oneOpt();
        }
        if(!optionalBbsBoard.isPresent()){
            optionalBbsBoard = bbsBoardService.lambdaQuery().like(BbsBoard::getBoardName, "测评").last("limit 1").oneOpt();
        }
        if(!optionalBbsBoard.isPresent()){
            return Collections.emptyList();
        }
        return lambdaQuery()
                .select(BbsPost::getId, BbsPost::getBoardId, BbsPost::getTitle, BbsPost::getCreateTime, BbsPost::getReplyCnt)
                .eq(BbsPost::getState, 1)
                .eq(BbsPost::getBoardId, optionalBbsBoard.orElse(null))
                .orderByDesc(BbsPost::getCreateTime)
                .orderByDesc(BbsPost::getReplyCnt)
                .last("limit 10")
                .list();
    }
}
